use crate::response::ApiResponse;
use axum::extract::rejection::{JsonRejection, PathRejection, QueryRejection};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum_valid::ValidRejection;

pub type ApiResult<T> = Result<T, ApiError>;
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
    #[error("Not Found")]
    NotFound,
    #[error("Method not allowed")]
    MethodNotAllowed,
    #[error("BusinessError: {0}")]
    BusinessError(String),
    #[error("Database Error: {0}")]
    DatabaseError(#[from] sea_orm::DbErr),
    #[error("Internal Server Error: {0}")]
    InternalError(#[from] anyhow::Error),
    #[error("QueryParams Bind Error: {0}")]
    QueryBindError(#[from] QueryRejection),
    #[error("PathParams Bind Error: {0}")]
    PathBindError(#[from] PathRejection),
    #[error("RequestBody Bind Error: {0}")]
    RequestBodyBindError(#[from] JsonRejection),
    #[error("Validate Params Error: {0}")]
    ValidationError(String),
    #[error("AuthServer Error: {0}")]
    AuthServerError(String),
    #[error("UnAuthenticated Error: {0}")]
    UnAuthenticatedError(String),
    #[error("URL Parse Error: {0}")]
    UrlParserError(#[from] url::ParseError),
    #[error("Request Error: {0}")]
    ReqWestError(#[from] reqwest::Error),
}
impl From<axum_valid::ValidRejection<ApiError>> for ApiError {
    fn from(value: ValidRejection<ApiError>) -> Self {
        match value {
            ValidRejection::Valid(errors) => ApiError::ValidationError(errors.to_string()),
            ValidRejection::Inner(error) => error,
        }
    }
}
impl ApiError {
    pub fn status_code(&self) -> StatusCode {
        match self {
            ApiError::NotFound => StatusCode::NOT_FOUND,
            ApiError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            ApiError::BusinessError(_) => StatusCode::OK,
            ApiError::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
            ApiError::DatabaseError(_) | ApiError::UrlParserError(_) | ApiError::ReqWestError(_) => StatusCode::INTERNAL_SERVER_ERROR,
            ApiError::QueryBindError(_) | ApiError::PathBindError(_)
            | ApiError::RequestBodyBindError(_) | ApiError::ValidationError(_) => StatusCode::BAD_REQUEST,
            ApiError::AuthServerError(_) | ApiError::UnAuthenticatedError(_) => StatusCode::UNAUTHORIZED,
        }
    }
}
impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let status_code = self.status_code();
        let body = axum::Json(ApiResponse::<()>::err(self.to_string()));
        (status_code, body).into_response()
    }
}